﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RPGProject.Data_Structures
{
    /// <summary>
    /// An implementation of the stack.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class Vector<T> : IEnumerable<T>
    {
        /// <summary>
        /// A delegate for a walked array function.
        /// </summary>
        /// <typeparam name="Q"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public delegate Q WalkFunc<Q>(T data);
        /// <summary>
        /// Array holding the data.
        /// </summary>
        private T[] data;
        /// <summary>
        /// The current size of the vector.
        /// </summary>
        private int size;
        /// <summary>
        /// Returns the current size of the vector.
        /// </summary>
        public int Count
        {
            get { return this.size; }
        }
        /// <summary>
        /// The current capacity of the vector.
        /// </summary>
        public int Capacity
        {
            get { return this.data.Count(); }
        }
        /// <summary>
        /// Creates a vector with the capacity specified, or if none is specified 10.
        /// </summary>
        /// <param name="capacity"></param>
        public Vector(int capacity = 10)
        {
            this.data = new T[capacity];
            this.size = 0;
        }
        /// <summary>
        /// Accesses and sets items at the index specified.  Throws IndexOutOfRangeException if index >= the array's size.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public T this[int index]
        {
            get { return this.data[index]; }
            set 
            {
                if (index >= this.size)
                {
                    throw new IndexOutOfRangeException();
                }
                this.data[index] = value; 
            }
        }
        /// <summary>
        /// Reset's the array to no items, size of 0, and resets capacity.
        /// </summary>
        public void Clear()
        {
            this.data = new T[10];
            this.size = 0;
        }
        /// <summary>
        /// Adds the the specified data to the top of the array.  Increases capacity if needed.  Increases size by 1.
        /// </summary>
        /// <param name="data"></param>
        public void Add(T data)
        {
            this.data[this.size] = data;
            this.size++;
            if (this.size >= this.Capacity)
            {
                this.Resize((int)(this.Capacity * 1.5));
            }
        }
        /// <summary>
        /// Helper resize method.
        /// </summary>
        /// <param name="p"></param>
        private void Resize(int p)
        {
            T[] newData = new T[p];
            for (int i = 0; i < this.Count; i++)
            {
                newData[i] = this[i];
            }
            this.data = newData;
        }
        /// <summary>
        /// Pushes data on top of a stack.
        /// </summary>
        /// <param name="data"></param>
        public void Push(T data)
        {
            this.Add(data);
        }
        /// <summary>
        /// Checks the top of the stack.
        /// </summary>
        /// <returns></returns>
        public T Peek()
        {
            return this.data[this.size - 1];
        }
        /// <summary>
        /// Pops the top element from the top of the stack.
        /// </summary>
        /// <returns></returns>
        public T Pop()
        {
            this.size--;
            T ret = this.data[this.size];
            return ret;
        }
        /// <summary>
        /// Returns an enumerator.
        /// </summary>
        /// <returns></returns>
        public IEnumerator<T> GetEnumerator()
        {
            for (int i = 0; i < this.size; i++)
            {
                yield return this[i];
            }
        }
        /// <summary>
        /// Returns an enumerator.
        /// </summary>
        /// <returns></returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public override string ToString()
        {
            String ret = "{";
            foreach (T test in this)
            {
                ret += test.ToString();
            }
            ret += " }";
            return ret;
        }
        /// <summary>
        /// Tests if the vector contains the data.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool Contains(T data)
        {
            foreach (T test in this)
            {
                if (test.Equals(data))
                    return true;
            }
            return false;
        }
        /// <summary>
        /// Walks through the array performing function wf.  Returns a vector of the returned values.
        /// </summary>
        /// <typeparam name="Q"></typeparam>
        /// <param name="wf"></param>
        /// <returns></returns>
        public Vector<Q> Walk<Q>(WalkFunc<Q> wf)
        {
            Vector<Q> ret = new Vector<Q>();
            foreach (T data in this)
            {
                ret.Add(wf(data));
            }
            return ret;
        }
    }
}
